package wangshuai.risk.algorithm;

import java.lang.Math;

/**
 *此算法是基于BP型反馈 神经网络
 *10个输入神经元，5个隐藏神经元 ，3个输出神经元
 *teachers[][]数组为 教师数组  是输出的期望值
 *inputsarr[]数值为输入值， 为测试方便， 先把要输入的放在数组里面
 *此算法还未完成，还在测试调整中
 */
public class BpNetT {
	static int IN_MUN = 10; // 输入层神经元数目
	static int HideN = 5; // 隐层神经元数目，前馈网络隐藏层
	static int OutN = 3; // 输出层神经元数模，各个命令的开关阀
	static int N = 5;
	static double Weight_In_Hide[][] = new double[HideN][IN_MUN]; // 输入层至隐层权值
	static double V_Hide_Out[][] = new double[OutN][HideN]; // 隐层至输出层权值
	static double YU_HN[] = new double[HideN]; // 隐层的阈值
	static double YU_ON[] = new double[OutN]; // 输出层的阈值
	static double X[] = new double[HideN]; // 隐层的输入,各个隐藏层的神经单元的内积
	static double Y[] = new double[OutN]; // 输出层的输入，各个输出层的神经单元的内积
	static double H[] = new double[HideN]; // 隐层的输出，s型函数之后的输出值
	static double O[] = new double[OutN]; // 输出层的输出，s型函数之后的输出值
	static double alpha; // 动量因子，改进型bp算法使用
	static double err_m[] = new double[N]; // 第m个样本的总误差
	static double a; // 输出层至隐层学习效率
	static double b; // 隐层至输入层学习效率
	static double d_err[] = new double[OutN]; // δk 隐藏层到输出层的误差
	static double e_err[] = new double[HideN]; // δj 输入层到隐藏层的误差

	static double old_W[][] = new double[HideN][IN_MUN]; // 新旧权值更新量交替--改进型Bp算法
	static double old_V[][] = new double[OutN][HideN];

	static double teach[] = new double[OutN]; // 教师样本
	static double input[] = new double[IN_MUN]; // 学生样本

	static double teachers[][] = { { 0.1f, 0.1f, 0.1f }, { 0.1f, 0.1f, 0.9f },
			{ 0.1f, 0.9f, 0.1f }, { 0.1f, 0.9f, 0.9f }, { 0.9f, 0.1f, 0.1f },
			{ 0.9f, 0.1f, 0.9f }, { 0.9f, 0.9f, 0.1f }, { 0.9f, 0.9f, 0.9f } };
	static double inputsarr[] = { 1f, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 1f, 0f }; // 学生标本

	/**
	 * 获取教师样本方法
	 */
	public boolean get_teach(int tm) {
		if (tm > 0 && tm < 9) {
			for (int i = 0; i < OutN; i++)
				teach[i] = teachers[tm][i];
			return true;
		} else
			return false;
	}

	/**
	 * 学生样本初始化
	 */
	public boolean get_pup() {
		for (int i = 0; i < IN_MUN; i++)
			input[i] = inputsarr[i];

		return true;
	}

	/**
	 * 初始化权、阈值子程序
	 */
	public boolean initial() {
		/* 初始化输入层到隐层的权值，随机模拟0.5~-0.5 */
		for (int x = 0; x < HideN; x++) {
			for (int q = 0; q < IN_MUN; q++) {

				Weight_In_Hide[x][q] = (double) (Math.random() - 0.5);

			}
		}

		/* 初始化隐层到输出层的权值，随机模拟0.5~-0.5 */
		for (int xx = 0; xx < OutN; xx++) {
			for (int qq = 0; qq < HideN; qq++) {

				V_Hide_Out[xx][qq] = (double) (Math.random() - 0.5);
			}
		}

		/* 隐层阈值初始化 */
		for (int z = 0; z < HideN; z++) {

			YU_HN[z] = (double) (Math.random() - 0.5);
		}

		/* 输出层阈值初始化 */
		for (int zz = 0; zz < OutN; zz++) {

			YU_ON[zz] = (double) (Math.random() - 0.5);
		}

		return true;
	}

	/**
	 * 求净输入，输出
	 */
	public boolean IN_OUT() {
		double sigma1, sigma2;
		for (int i = 0; i < HideN; i++) {
			sigma1 = 0.0f;
			/* 求隐层内积 */
			for (int j = 0; j < IN_MUN; j++)
				sigma1 = sigma1 + Weight_In_Hide[i][j] * input[j];

			X[i] = sigma1 + YU_HN[i]; /* 权值*输入值 再 加上阈值 */
			H[i] = (1.0 / (1.0 + Math.exp(-X[i]))); /*
													 * 隐层内积的s型函数输出
													 * Math.exp(double x)
													 */
		}

		for (int ii = 0; ii < OutN; ii++) {
			sigma2 = 0.0f;
			for (int jj = 0; jj < HideN; jj++)
				sigma2 = sigma2 + V_Hide_Out[ii][jj] * H[jj];

			Y[ii] = sigma2 + YU_ON[ii];
			O[ii] = (1.0 / (1.0 + Math.exp(-Y[ii]))); /*
													 * 输出层内积的s型函数输出
													 * Math.exp(double x)
													 */
		}

		return true;
	}

	/*
	 * 误差分析 隐藏层到输出层的误差 δk
	 */

	public boolean Err_O_H(int ml) {
		double abs_err[] = new double[OutN];
		double sqr_err = 0.0f;
		for (int k = 0; k < OutN; k++) {
			abs_err[k] = teach[k] - O[k]; // 误差信号 e(n)=d(n)-y(n)
			sqr_err = sqr_err + (abs_err[k]) * (abs_err[k]);
			d_err[k] = abs_err[k] * O[k] * (1.0 - O[k]); /*
														 * 教师值与输出值的差 乘以 输出值 乘以
														 * (1-输出值)
														 */
		}

		err_m[ml] = sqr_err / 2; /* 输出层的每个输出神经元的 教师值与输出值的差 的和 除以2的值 */

		return true;
	}

	/**
	 * 输入层到隐藏层的误差 δj
	 */
	public boolean Err_H_I() {
		double sigma;
		for (int j = 0; j < HideN; j++) {
			sigma = 0.0f;
			for (int k = 0; k < OutN; k++)
				sigma = sigma + d_err[k] * V_Hide_Out[k][j];

			e_err[j] = sigma * H[j] * (1 - H[j]);
		}
		return true;
	}

	/* 总误差，当需要批量训练的时候才用 */
	public double Err_Sum() {
		double total_err = 0.0f;
		for (int m = 0; m < N; m++)
			total_err = total_err + err_m[m];

		return total_err;
	}

	/**
	 * 新旧权值更新量交替--改进型Bp算法
	 */
	public boolean saveWV() {

		for (int i = 0; i < HideN; i++) {
			for (int j = 0; j < IN_MUN; j++)
				old_W[i][j] = b * e_err[i] * input[j]; // 输入层到隐层的权值更新
		}
		for (int ii = 0; ii < OutN; ii++) {
			for (int jj = 0; jj < HideN; jj++)
				old_V[ii][jj] = a * d_err[ii] * H[jj]; // 隐层到输出层的权值更新
		}
		return true;
	}

	/* 更新权值，阈值 */
	/* 输出层更新 */
	public boolean Updata_O_H() {
		for (int k = 0; k < OutN; k++) {
			for (int j = 0; j < HideN; j++)
				// V_Hide_Out[k][j]=V_Hide_Out[k][j]+a*d_err[k]*H[j];
				// V_Hide_Out[k][j]=V_Hide_Out[k][j]+(1.0-alpha)*a*d_err[k]*H[j]+alpha*a*d_err[k]*H[j];
				V_Hide_Out[k][j] = V_Hide_Out[k][j] + (1.0 - alpha) * a
						* d_err[k] * H[j] + alpha * old_V[k][j];
			YU_ON[k] = YU_ON[k] + a * d_err[k];
		}
		return true;
	}

	/* 隐层层更新 */
	public boolean Updata_H_I() {
		for (int j = 0; j < HideN; j++) {
			for (int i = 0; i < IN_MUN; i++)
				// Weight_In_Hide[j][i]=Weight_In_Hide[j][i]+b*e_err[j]*input[i];
				// Weight_In_Hide[j][i]=Weight_In_Hide[j][i]+(1.0-alpha)*b*e_err[j]*input[i]+alpha*b*e_err[j]*input[i];
				Weight_In_Hide[j][i] = Weight_In_Hide[j][i] + (1.0 - alpha) * b
						* e_err[j] * input[i] + alpha * old_W[j][i];
			YU_HN[j] = YU_HN[j] + b * e_err[j];
		}

		return true;
	}

	/**
	 * 打印计算结果
	 */
	public void Print_Result() {

		System.out.println("输入层到隐藏层权值：");
		for (int i = 0; i < HideN; i++)
			for (int j = 0; j < IN_MUN; j++)
				System.out.println("Weight_In_Hide[" + i + "]" + "[" + j + "]"
						+ "=" + Weight_In_Hide[i][j]);

		System.out.println("");
		System.out.println("隐藏层到输出层权值：");
		for (int i = 0; i < OutN; i++)
			for (int j = 0; j < HideN; j++)
				System.out.println("V_Hide_Out[" + i + "]" + "[" + j + "]"
						+ "=" + V_Hide_Out[i][j]);

		System.out.println("");
		System.out.println("隐藏层阈值：");
		for (int i = 0; i < HideN; i++)
			System.out.println("YU_HN[" + i + "]=" + YU_HN[i]);

		System.out.println("");
		System.out.println("输出层阈值：");
		for (int i = 0; i < OutN; i++)
			System.out.println("YU_ON[" + i + "]=" + YU_ON[i]);

		System.out.println("");
		System.out.println("输出层输出值：");
		for (int i = 0; i < OutN; i++)
			System.out.println("O[" + i + "]=" + O[i]);

	}

	public static void main(String[] args) {
		alpha = 0.2f; // 动量因子
		double a = 0.5f; // 0~1之间 ，趋于0就比较保守，趋于1就比较激进
		double b = 0.5f; // 0~1之间 ，趋于0就比较保守，趋于1就比较激进
		int flag = 200000; // 学习的极限次数
		int study = 0; // 学习次数
		double Pre_error = 0.00001f; // 可接受误差
		double sum_err; // 总误差

		BpNetT newsjwl = new BpNetT();
		newsjwl.get_teach(1); // 选择老师
		newsjwl.get_pup(); // 学生标本
		newsjwl.initial(); // 网络元初始化

		do {
			++study;
			newsjwl.IN_OUT(); // 计算权值阈值
			newsjwl.Err_O_H(0); // 隐藏层到输出层误差
			newsjwl.Err_H_I(); // 输入层到隐藏层误差
			newsjwl.Updata_O_H(); // 更新隐藏层到输出层的权值阈值
			newsjwl.Updata_H_I(); // 更新输入层到隐藏层的权值阈值
			newsjwl.saveWV();
			sum_err = newsjwl.Err_Sum(); // 计算总误差
			if (study > flag) {
				break;
			}

		} while (sum_err > Pre_error); // 可接受误差和真实总误差的比较

		newsjwl.Print_Result(); // 输出权值阈值

		/*
		 * 
		 * System.out.println(""); System.out.println("学习次数："+study);
		 * System.out.println(sum_err); System.out.println("e_err[i]:"); for(int
		 * i=0;i<HideN;i++) System.out.println(e_err[i]);
		 * System.out.println("d_err[i]:"); for(int i=0;i<OutN;i++)
		 * System.out.println(d_err[i]); System.out.println("");
		 */
	}

}
